/*
 * ModelCC, distributed under ModelCC Shared Software License, www.modelcc.org
 */

package org.modelcc.language.syntax;

import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

/**
 * Symbol.
 * 
 * @author Luis Quesada (lquesada@modelcc.org), partially refactored by Fernando Berzal (berzal@modelcc.org)
 */
public final class Symbol implements Serializable 
{
    /**
     * id
     */
    private int id;
    
    /**
     * Contents.
     */
    private List<Symbol> contents;

    /**
     * Rule.
     */
    private Rule rule;

    /**
     * Relevant rule (not E ::= E rule).
     */
    private Rule relevantRule;

    /**
     * List of elements.
     */
    private List<RuleSymbol> elements;

    /**
     * User data.
     */
    private Object userData;

    /**
     * The input symbol.
     */
    private InputSymbol input;
    
    /**
     * Constructor.
     * @param id The symbol id.
     * @param ps The input symbol.
     */
    public Symbol(int id, InputSymbol input) 
    {
        this.id = id;
        this.input = input;
        this.userData = input.getUserData();        
        this.rule = null;
        this.elements = new ArrayList<RuleSymbol>();
        this.contents = new ArrayList<Symbol>();
    }

    /**
     * Constructor.
     * @param id the symbol id.
     * @param input the input symbol.
     * @param rule the applied rule applied.
     * @param relevantRule the relevant rule applied.
     * @param elements the used rule elements.
     * @param contents the contents.
     */
    public Symbol(int id, InputSymbol input,Rule rule,List<RuleSymbol> elements,List<Symbol> contents) 
    {
        this.id = id;
        this.input = input;
        this.userData = input.getUserData();
        this.rule = rule;
        this.relevantRule = findRelevantRule(contents, rule);
        this.elements = new ArrayList<RuleSymbol>(elements);
        this.contents = new ArrayList<Symbol>(contents);
    }


    // Getters
    
    public int getId() 
    {
        return id;
    }
    
    public Object getType() 
    {
        return input.getType();
    }

    public int size ()
    {
    	return contents.size();  // == elements.size()
    }
    
    
    public List<Symbol> getContents() 
    {
        return Collections.unmodifiableList(contents);
    }
    
    public Symbol getContent (int index)
    {
    	return contents.get(index);
    }

    // User data

    public Object getUserData() 
    {
        return userData;
    }

    public void setUserData(Object userData) 
    {
        this.userData = userData;
    }

    // Rule

    /**
     * @return the rule
     */
    public Rule getRule() 
    {
        return rule;
    }

    /**
     * @return the relevant rule
     */
    public Rule getRelevantRule() 
    {
        return relevantRule;
    }
    
	private Rule findRelevantRule (List<Symbol> content, Rule r) 
	{
		Rule relevant;
		
		if (r.getRight().size()==1)
		    relevant = content.get(0).getRelevantRule();
		else
		    relevant = r;
		
		return relevant;
	}
    
    
    /**
     * @return the matched rule elements
     */
    public List<RuleSymbol> getElements() 
    {
        return Collections.unmodifiableList(elements);
    }
    
    public RuleSymbol getElement (int index)
    {
    	return elements.get(index);
    }


    // Input symbol
    
    /**
     * @return the input symbol.
     */
    public InputSymbol getInputSymbol() 
    {
        return input;
    }

    /**
     * @return the start index
     */
    public int getStartIndex() 
    {
        return input.getStartIndex();
    }

    /**
     * @return the end index
     */
    public int getEndIndex() 
    {
        return input.getEndIndex();
    }


    // Object methods
    
    @Override
    public boolean equals(Object obj) 
    {
        if (obj == null) {
            return false;
        }
        if (getClass() != obj.getClass()) {
            return false;
        }
        final Symbol other = (Symbol) obj;
        return this.id == other.id;
    }

    @Override
    public int hashCode() 
    {
        return this.id;
    }

    @Override
    public String toString ()
    {
    	return "[Symbol "+id+"] " + input + " - " + userData;
    }
}
